﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using OpenExpressApp.Types;
using OpenExpressApp.MetaModel;
using System.Diagnostics;
using System.ComponentModel;

namespace OpenExpressApp.Module.WPF
{
    public class NavigateQueryObjectView : QueryObjectView
    {
        private IList<NavigateProperty> _navigateProperties;

        public NavigateQueryObjectView(Type boType, ObjectView resultView)
            : base(boType, resultView)
        {
            this._navigateProperties = new List<NavigateProperty>();
            //this._naviObjectProperty = string.Empty;
            this.RegionType = RegionType.NavigateQuery;

            this.InitNavigateProperties(boType);
        }

        /// <summary>
        /// 这个导航面板使用的所有的导航属性。
        /// </summary>
        public IList<NavigateProperty> NavigateProperties
        {
            get
            {
                return _navigateProperties;
            }
        }

        /// <summary>
        /// 使用这个查询面板中的查询对象数据，
        /// 给newObject的外键设置值
        /// </summary>
        /// <param name="newObject"></param>
        public void SetReferenceEntity(object referenceEntity)
        {
            if (referenceEntity == null) throw new ArgumentNullException("referenceEntity");

            var currentObject = this.CurrentObject;

            //对每一个导航的实体引用属性，都给referenceEntity赋相应的值
            foreach (var naviProperty in this._navigateProperties)
            {
                if (naviProperty.IsEntityId)
                {
                    var idProperty = naviProperty.Property;
                    var entityProperty = naviProperty.EntityProperty;

                    //读值
                    var idValue = currentObject.GetPropertyValue(idProperty);
                    var entityValue = currentObject.GetPropertyValue(entityProperty);

                    //尝试写值到新对象中
                    this.SafeWriteValue(referenceEntity, idProperty, idValue);
                    this.SafeWriteValue(referenceEntity, entityProperty, entityValue);
                }
            }
        }
        /// <summary>
        /// 通过外键引用实体，把CurrentObject相应的属性设置好。
        /// </summary>
        /// <param name="propertyName">此面板对应的实体类型的实体引用属性名</param>
        /// <param name="referenceEntity">被引用的实体对象</param>
        public void SetByReferenceEntity(string propertyName, object referenceEntity)
        {
            if (string.IsNullOrEmpty(propertyName)) throw new ArgumentNullException("propertyName");
            if (referenceEntity == null) throw new ArgumentNullException("referenceEntity");

            //根据propertyName在_navigateProperties列表中，查找已经得到的导航属性。
            var naviProperty = this.FindNaviProperty(propertyName);
            if (naviProperty != null)
            {
                //这个导航属性必须是外键引用实体属性。
                if (!(naviProperty.IsEntityId)) throw new InvalidOperationException("naviProperty.IsEntityId");

                var currentObject = this.CurrentObject;

                if (currentObject != null)
                {
                    //设置实体引用Id
                    var value = referenceEntity.GetPropertyValue(DBConvention.FieldName_Id);
                    this.SafeWriteValue(currentObject, naviProperty.EntityProperty, referenceEntity);
                    this.SafeWriteValue(currentObject, naviProperty.Property, value);
                }
            }
        }

        protected override void OnCurrentObjectPropertyChanged(PropertyChangedEventArgs e)
        {
            base.OnCurrentObjectPropertyChanged(e);

            //如果任何一个导航属性发生改变，则执行查询。
            if (this._navigateProperties.Any(p => p.Property == e.PropertyName))
            {
                this.ExecuteQuery();
            }
        }

        private void InitNavigateProperties(Type boType)
        {
            IEntityInfo entityInfo = ApplicationModel.GetBusinessObjectInfo(boType);

            //列表导航属性
            foreach (var propertyInfo in entityInfo.BOsPropertyInfos)
            {
                if (propertyInfo.ViewInfo().IsNavigateQueryItem)
                {
                    var naviProperty = this.Convert(propertyInfo);
                    this._navigateProperties.Add(naviProperty);
                }
            }

            //Lookup属性导航
            foreach (var propertyInfo in entityInfo.BOPropertyInfos)
            {
                if (propertyInfo.ViewInfo().IsNavigateQueryItem)
                {
                    var naviProperty = this.Convert(propertyInfo);
                    this._navigateProperties.Add(naviProperty);
                }
            }
        }
        /// <summary>
        /// 根据某一属性名查找转换后的NavigateProperty
        /// </summary>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        private NavigateProperty FindNaviProperty(string propertyName)
        {
            NavigateProperty toFind = null;

            Type boType = this.BOType;
            IEntityInfo entityInfo = ApplicationModel.GetBusinessObjectInfo(boType);

            //列表导航属性
            foreach (var propertyInfo in entityInfo.BOsPropertyInfos)
            {
                if (propertyInfo.ViewInfo().IsNavigateQueryItem && propertyInfo.Name == propertyName)
                {
                    toFind = this.Convert(propertyInfo);
                    break;
                }
            }

            //Lookup属性导航
            if (toFind == null)
            {
                foreach (var propertyInfo in entityInfo.BOPropertyInfos)
                {
                    if (propertyInfo.ViewInfo().IsNavigateQueryItem && propertyInfo.Name == propertyName)
                    {
                        toFind = this.Convert(propertyInfo);
                        break;
                    }
                }
            }

            NavigateProperty result = null;

            if (toFind != null)
            {
                result = this._navigateProperties.First(p => p.Property == toFind.Property);
            }

            return result;
        }
        /// <summary>
        /// 把一个关联的孩子属性转换为导航属性
        /// </summary>
        /// <param name="property"></param>
        /// <returns></returns>
        private NavigateProperty Convert(IAssociationPropertyInfo property)
        {
            var propertyInfo = property.ViewInfo();

            var queryItemAttri = propertyInfo.QueryItemValueTypeAttribute;
            if (queryItemAttri == null)
            {
                return new NavigateProperty(propertyInfo.Name);
            }

            if (queryItemAttri.QueryItemValueType != QueryItemValueType.Id)
            {
                throw new NotSupportedException("暂时不支持其实的查询项值类型。");
            }

            //实体属性名就是属性的类型名
            var entityProperty = property.ChildEntityType.Name;
            var idProperty = queryItemAttri.IdPropertyName;
            if (string.IsNullOrEmpty(idProperty))
            {
                //对象名+Id
                idProperty = entityProperty + DBConvention.FieldName_Id;
            }

            return new NavigateProperty(idProperty, entityProperty);
        }
        /// <summary>
        /// 把一个一般的属性转换为导航属性
        /// </summary>
        /// <param name="property"></param>
        /// <returns></returns>
        private NavigateProperty Convert(IEntityPropertyInfo property)
        {
            var propertyInfo = property.ViewInfo();

            var queryItemAttri = propertyInfo.QueryItemValueTypeAttribute;
            if (queryItemAttri == null)
            {
                return new NavigateProperty(propertyInfo.Name);
            }

            if (queryItemAttri.QueryItemValueType != QueryItemValueType.Id)
            {
                throw new NotSupportedException("暂时不支持其实的查询项值类型。");
            }

            string entityProperty = propertyInfo.LookupAttribute.LookupPropertyName;
            string idProperty = queryItemAttri.IdPropertyName;
            if (string.IsNullOrEmpty(idProperty))
            {
                //直接使用属性名
                idProperty = propertyInfo.Name;
            }

            return new NavigateProperty(idProperty, entityProperty);
        }
        private void SafeWriteValue(object obj, string propertyName, object value)
        {
            PropertyInfo p = obj.GetType().GetProperty(propertyName);
            if (p != null && p.CanWrite)
            {
                p.SetValue(obj, value, null);
            }
        }

        #region NavigateProperty

        /// <summary>
        /// 导航属性
        /// 
        /// 可以是如下两种形式：
        /// Property（一般的属性）
        /// IdProperty + EntityProperty（实体引用属性）
        /// </summary>
        public class NavigateProperty
        {
            private string _property;
            private string _entityProperty;

            public NavigateProperty(string propertyName)
            {
                if (string.IsNullOrEmpty(propertyName)) throw new ArgumentNullException("propertyName");

                this._property = propertyName;
            }
            public NavigateProperty(string idProperty, string entityProperty)
            {
                if (string.IsNullOrEmpty(idProperty)) throw new ArgumentNullException("idProperty");
                if (string.IsNullOrEmpty(entityProperty)) throw new ArgumentNullException("entityProperty");

                this._property = idProperty;
                this._entityProperty = entityProperty;
            }

            /// <summary>
            /// 如果这个值为真，表示这是一个实体引用的id属性。
            /// 此时，EntityProperty表示实体引用属性名。
            /// </summary>
            public bool IsEntityId
            {
                get
                {
                    return string.IsNullOrEmpty(this._entityProperty) == false;
                }
            }
            /// <summary>
            /// 属性名
            /// </summary>
            public string Property
            {
                get
                {
                    return this._property;
                }
            }
            /// <summary>
            /// 如果IsEntityId为true，此属性表示表示实体引用属性名。
            /// </summary>
            public string EntityProperty
            {
                get
                {
                    return this._entityProperty;
                }
            }
        }

        #endregion
    }
}